home *** CD-ROM | disk | FTP | other *** search
/ Amiga Format CD 41 / Amiga Format CD41 (1999-06)(Future Publishing)(GB)[!][issue 1999-07].iso / -seriously_amiga- / graphics / mountainview / mountain.c < prev    next >
C/C++ Source or Header  |  1999-04-28  |  31KB  |  904 lines

  1. /*------------------------------------------------------------------------*\
  2.  |                "Fractal Mountains" landscape generator.                |
  3.  *------------------------------------------------------------------------*
  4.  |              Original algorithm and BASIC implementation               |
  5.  |                   © 1987 Compute Publications, Inc.                    |
  6.  |                                                                        |
  7.  |                 KickPascal conversion by W. Nöker 1992                 |
  8.  |           C conversion and interactive GUI by W. Nöker 1999            |
  9.  *------------------------------------------------------------------------*
  10.  |                  Wilhelm Nöker <wnoeker@t-online.de>                   |
  11. \*------------------------------------------------------------------------*/
  12.  
  13.  
  14. #include <stdio.h>
  15. #include <string.h>
  16. #include <stdlib.h>
  17. #include <time.h>
  18. #include <math.h>
  19. #include <proto/exec.h>
  20. #include <proto/intuition.h>
  21. #include <proto/gadtools.h>
  22. #include <proto/graphics.h>
  23. #undef GetOutlinePen    /* suppress a warning for the gcc include files */
  24. #include <graphics/gfxmacros.h>
  25. #include "gui.h"
  26.  
  27. typedef double REAL;
  28. #define MAPSIZE 64
  29.  
  30. REAL raMap[ MAPSIZE+1 ][ MAPSIZE+1 ];
  31. REAL rPeak, rSnowLine;
  32. BOOL fBorder = FALSE;
  33. WORD wHotGadget = IDC_NONE;
  34.  
  35. BOOL CheckAbort();
  36.  
  37.  
  38. /*------------------------------------------------------------------------*\
  39.  
  40.   Generate a random number 0.0 < r < 1.0.
  41.   Calling srand( time( NULL ) ) is recommended to seed the generator.
  42.  
  43. \*------------------------------------------------------------------------*/
  44.  
  45. REAL Random()
  46.     {
  47.     return rand() / (RAND_MAX + 1.0);
  48.     }
  49.  
  50.  
  51.  
  52. /*------------------------------------------------------------------------*\
  53.  
  54.   Recalculate map levels * using some of their neighbours +.
  55.   The following patterns are applied:
  56.  
  57.         DoColumns()     DoRows()        DoCentres()
  58.  
  59.         + * + * +       + · + · +       · + · + ·
  60.         · · · · ·       * · * · *       + * + * +
  61.         + * + * +       + · + · +       · + · + ·
  62.         · · · · ·       * · * · *       + * + * +
  63.         + * + * +       + · + · +       · + · + ·
  64.  
  65.   The wGrid parameter is the granularity (distance between two *'s) and
  66.   is decreased with each iteration, starting at MAPSIZE, ending at 2.
  67.   The above diagrams show the 2nd iteration (i.e. wGrid == MAPSIZE / 2).
  68.  
  69. \*------------------------------------------------------------------------*/
  70.  
  71. VOID DoColumns( WORD wGrid, REAL rScale )
  72.     {
  73.     WORD wX, wY, nHalf = wGrid / 2;
  74.  
  75.     for( wY = 0; wY <= MAPSIZE; wY += wGrid )
  76.         for( wX = nHalf; wX <= MAPSIZE; wX += wGrid )
  77.             {
  78.             raMap[ wX ][ wY ] =
  79.                 (raMap[ wX-nHalf ][ wY ] + raMap[ wX+nHalf ][ wY ]) / 2
  80.                 + (Random() - 0.5) * wGrid * rScale;
  81.             }
  82.     }
  83.  
  84.  
  85. VOID DoRows( WORD wGrid, REAL rScale )
  86.     {
  87.     WORD wX, wY, nHalf = wGrid / 2;
  88.  
  89.     for( wX = 0; wX <= MAPSIZE; wX += wGrid )
  90.         for( wY = nHalf; wY <= MAPSIZE; wY += wGrid )
  91.             {
  92.             raMap[ wX ][ wY ] =
  93.                 (raMap[ wX ][ wY-nHalf ] + raMap[ wX ][ wY+nHalf ]) / 2
  94.                 + (Random() - 0.5) * wGrid * rScale;
  95.             }
  96.     }
  97.  
  98.  
  99. VOID DoCentres( WORD wGrid, REAL rScale )
  100.     {
  101.     WORD wX, wY, nHalf = wGrid / 2;
  102.  
  103.     for( wX = nHalf; wX <= MAPSIZE; wX += wGrid )
  104.         for( wY = nHalf; wY <= MAPSIZE; wY += wGrid )
  105.             {
  106.             raMap[ wX ][ wY ] =
  107.                 (raMap[ wX-nHalf ][ wY ] + raMap[ wX+nHalf ][ wY ]
  108.                + raMap[ wX ][ wY-nHalf ] + raMap[ wX ][ wY+nHalf ]) / 4
  109.                 + (Random() - 0.5) * wGrid * rScale;
  110.             }
  111.     }
  112.  
  113.  
  114.  
  115. /*------------------------------------------------------------------------*\
  116.  
  117.   Generate a random map. The larger the rScale parameter, the "craggier"
  118.   it will look.
  119.  
  120. \*------------------------------------------------------------------------*/
  121.  
  122. VOID MakeMountain( REAL rScale )
  123.     {
  124.     WORD wX, wY, wGrid;
  125.     WORD wArea, wOcean, wIce;
  126.     static char caTitle[ 80 ];
  127.  
  128.     srand( time( NULL ) );
  129.     for( wX = 0; wX <= MAPSIZE; wX++ )
  130.         for( wY = 0; wY <= MAPSIZE; wY++ )
  131.             raMap[ wX ][ wY ] = 0;
  132.     for( wGrid = MAPSIZE; wGrid >= 2; wGrid /= 2 )
  133.         {
  134.         sprintf( caTitle, "Building landscape (%02d)...", wGrid );
  135.         SetWindowTitles( pwinMain, caTitle, (STRPTR)-1 );
  136.         DoColumns( wGrid, rScale );
  137.         DoRows( wGrid, rScale );
  138.         DoCentres( wGrid, rScale );
  139.         if( CheckAbort() )
  140.             break;
  141.         }
  142.  
  143.     /* Determine the peak height. */
  144.     rPeak = 0;
  145.     for( wX = 0; wX <= MAPSIZE; wX++ )
  146.         for( wY = 0; wY <= MAPSIZE; wY++ )
  147.             if( rPeak < raMap[ wX ][ wY ] )
  148.                 rPeak = raMap[ wX ][ wY ];
  149.     rSnowLine = 0.75 * rPeak;
  150.  
  151.     /* Calculate (and display) some statistics. */
  152.     wArea = wOcean = wIce = 0;
  153.     for( wX = 0; wX <= MAPSIZE; wX++ )
  154.         for( wY = 0; wY <= MAPSIZE; wY++ )
  155.             {
  156.             wArea++;
  157.             if( raMap[ wX ][ wY ] < 0 )
  158.                 wOcean++;
  159.             if( raMap[ wX ][ wY ] > rSnowLine )
  160.                 wIce++;
  161.             }
  162.     if( wGrid > 2 )
  163.         strcat( caTitle, " aborted" );
  164.     else
  165.         sprintf( caTitle, "Ocean: %d %%, Ice: %d %%",
  166.             (100 * wOcean) / wArea, (100 * wIce) / wArea );
  167.     SetWindowTitles( pwinMain, caTitle, (STRPTR)-1 );
  168.     }
  169.  
  170.  
  171.  
  172. /*------------------------------------------------------------------------*\
  173.  
  174.   Calculate the color for one triangle in the fractal surface, the steeper
  175.   the darker. To be precise: the illumination is proportional to the
  176.   z-component of the normalized cross-product of two of the triangle's edge
  177.   vectors. And because of the regular layout of the x/y-coordinates, which
  178.   always mark an isosceles, right-angled triangle, for example:
  179.  
  180.         ^
  181.         |
  182.     y+1 +   z1 *-----*
  183.         |      |\   /|
  184.         |      | \ z0|
  185.         |      |  *  |
  186.         |      | / \ |
  187.         |      |/   \|
  188.      y  +   z2 *-----*
  189.         |
  190.         +------+-----+----->
  191.                x    x+1
  192.  
  193.   everything boils down to the rather boring formula that you will find
  194.   below.
  195.  
  196.   Additional rules: If all z-values are above the snowline, the color will
  197.   be gray instead of brown, if they are below zero, the color will be blue.
  198.  
  199.   The returned pen number is intended for the following color map:
  200.          0: black
  201.      1..15: brown (dark to light)
  202.         16: blue
  203.     17..31: gray (dark gray to white)
  204.  
  205. \*------------------------------------------------------------------------*/
  206.  
  207. WORD GetShadePen( REAL rZ0, REAL rZ1, REAL rZ2, REAL rSnowLine )
  208.     {
  209.     REAL rLight;
  210.     WORD wPen = 1;
  211.  
  212.     if( rZ0 <= 0 && rZ1 <= 0 && rZ2 <= 0 )
  213.         return 16;              /* deep blue sea */
  214.     if( rZ0 > rSnowLine && rZ1 > rSnowLine && rZ2 > rSnowLine )
  215.         wPen += 16;             /* snow peaked mountains */
  216.     rZ1 -= rZ0;
  217.     rZ2 -= rZ0;
  218.     rLight = 1 / (1 + 2*rZ1*rZ1 + 2*rZ2*rZ2);
  219.     return wPen + (WORD)(14 * rLight + 0.5);
  220.     }
  221.  
  222.  
  223.  
  224. /*------------------------------------------------------------------------*\
  225.  
  226.   Transform map coordinates into window coordinates. This will look
  227.   somehow like this:
  228.      _________________________________________________
  229.     |·|____________________________________________|__|
  230.     ||                                              | |
  231.     ||  \ y                                         | |
  232.     ||                                              | |
  233.     ||    +-MAPSIZE--------------+                  | |
  234.     ||     \                       \                | |
  235.     ||      \                        \              | |
  236.     ||       \                         \            | |
  237.     ||        \                          \          | |
  238.     ||       0 +--------------------------+  -> x   | |
  239.     ||         0                       MAPSIZE      | |
  240.     ||                                              |_|
  241.     ++==============================================|/|
  242.  
  243. \*------------------------------------------------------------------------*/
  244.  
  245. typedef struct
  246.     {
  247.     REAL x;
  248.     REAL y;
  249.     REAL z;
  250.     } VECTOR;
  251.  
  252. struct                  /* Vectors for projection */
  253.     {
  254.     VECTOR f;           /* the point we focus on */
  255.     VECTOR o;           /* the observer */
  256.     VECTOR l;           /* the line of sight */
  257.     VECTOR h;           /* horizon on our silver screen */
  258.     VECTOR v;           /* vertical line */
  259.     REAL rXScale;
  260.     REAL rYScale;
  261.     WORD wXOff;
  262.     WORD wYOff;
  263.     } prj;
  264.  
  265.  
  266. /*------------------------------------------------------------------------*\
  267.  
  268.   Set a new postion for the observer, in spherical coordinates around
  269.   the center of our landscape. Parameters:
  270.  
  271.     rR - radius, in multiples of the landscape's diameter
  272.     rPhi - angle around the z-axis, 0..360 degrees
  273.     rTheta - angle against the z-axis, 0..180 degrees
  274.  
  275. \*------------------------------------------------------------------------*/
  276.  
  277. VOID SetObserver( REAL rR, REAL rPhi, REAL rTheta )
  278.     {
  279.     REAL rDeg2Rad = 3.14159 / 180;
  280.  
  281.     rR *= sqrt( 2 * MAPSIZE*MAPSIZE + rPeak*rPeak );
  282.     rPhi *= rDeg2Rad;
  283.     rTheta *= rDeg2Rad;
  284.     /* Focus shall be in the middle of the chunk of landscape. */
  285.     prj.f.x = MAPSIZE / 2;
  286.     prj.f.y = MAPSIZE / 2;
  287.     prj.f.z = rPeak / 2;
  288.     /* The observer's line of sight. */
  289.     prj.l.x = -cos( rPhi )*sin( rTheta );
  290.     prj.l.y = -sin( rPhi )*sin( rTheta );
  291.     prj.l.z = -cos( rTheta );
  292.     /* Their position in absolute x, y, z. */
  293.     prj.o.x = prj.f.x - rR * prj.l.x;
  294.     prj.o.y = prj.f.y - rR * prj.l.y;
  295.     prj.o.z = prj.f.z - rR * prj.l.z;
  296.     /* Base vectors of the projection plane. */
  297.     prj.h.x = -sin( rPhi );
  298.     prj.h.y =  cos( rPhi );
  299.     prj.h.z =  0;
  300.     prj.v.x =  cos( rTheta )*cos( rPhi );
  301.     prj.v.y =  cos( rTheta )*sin( rPhi );
  302.     prj.v.z = -sin( rTheta );
  303.     }
  304.  
  305. VOID Transform( REAL rX, REAL rY, REAL rZ, WORD *pwX, WORD *pwY )
  306.     {
  307.     VECTOR v;
  308.     REAL r;
  309.     if( rZ < 0)
  310.         rZ = 0;
  311.     /* Calculate a vector from observer to target, */
  312.     /* normalize it with respect to the observer's line of sight. */
  313.     r = (rX - prj.o.x)*prj.l.x + (rY - prj.o.y)*prj.l.y + (rZ - prj.o.z)*prj.l.z;
  314.     v.x = (rX - prj.o.x) / r;
  315.     v.y = (rY - prj.o.y) / r;
  316.     v.z = (rZ - prj.o.z) / r;
  317.     /* Do the projection and adjust for screen display. */
  318.     *pwX = prj.wXOff + prj.rXScale *
  319.             (v.x*prj.h.x + v.y*prj.h.y + v.z*prj.h.z);
  320.     *pwY = prj.wYOff + prj.rYScale *
  321.             (v.x*prj.v.x + v.y*prj.v.y + v.z*prj.v.z);
  322.     }
  323.  
  324.  
  325.  
  326. /*------------------------------------------------------------------------*\
  327.  
  328.   Adjust projection scaling and offsets, so that the eight vertices of
  329.   the cuboid surrounding our landscape best fit into the available drawing
  330.   area.
  331.  
  332. \*------------------------------------------------------------------------*/
  333.  
  334. VOID MakeScales( WORD wXMin, WORD wXMax, WORD wYMin, WORD wYMax )
  335.     {
  336.     REAL rMag, rTmp;
  337.     REAL rX, rY, rZ;
  338.     WORD wX, wY;
  339.     WORD wX0, wX1, wY0, wY1;
  340.     WORD wIdx, wRun;
  341.     WORD wXSize = wXMax - wXMin, wYSize = wYMax - wYMin;
  342.  
  343.     /* We calculate the required magnification by iteration. */
  344.     /* The reason to do so is a little pathetic: Although the formula */
  345.     /* itself is precise, we have to deal with rounding errors, as we */
  346.     /* chose to let the coordinate transformation return integer values. */
  347.     wX0 = wX1 = wY0 = wY1 = 0;  /* suppress optimizer warning */
  348.  
  349.     /* "First guess" for scaling and offsets. */
  350.     /* If a skewed x/y scaling ratio is required, include it here. */
  351.     prj.rXScale =
  352.     prj.rYScale = wXSize;
  353.     prj.wXOff = 0;
  354.     prj.wYOff = 0;
  355.     for( wRun = 0; wRun < 4; wRun++ )
  356.         {
  357.         /* Make projections of the eight vertices. */
  358.         rX = rY = rZ = 0;
  359.         for( wIdx = 0; wIdx < 8; wIdx++ )
  360.             {
  361.             Transform( rX, rY, rZ, &wX, &wY );
  362.             if( wX0 > wX || wIdx == 0 )
  363.                 wX0 = wX;
  364.             if( wY0 > wY || wIdx == 0 )
  365.                 wY0 = wY;
  366.             if( wX1 < wX || wIdx == 0 )
  367.                 wX1 = wX;
  368.             if( wY1 < wY || wIdx == 0 )
  369.                 wY1 = wY;
  370.             switch( wIdx )
  371.                 {
  372.                 case 0:
  373.                     rX += MAPSIZE+1;
  374.                     break;
  375.                 case 1:
  376.                     rY += MAPSIZE+1;
  377.                     break;
  378.                 case 2:
  379.                     rX -= MAPSIZE+1;
  380.                     break;
  381.                 case 3:
  382.                     rZ += rPeak;
  383.                     break;
  384.                 case 4:
  385.                     rX += MAPSIZE+1;
  386.                     break;
  387.                 case 5:
  388.                     rY -= MAPSIZE+1;
  389.                     break;
  390.                 case 6:
  391.                     rX -= MAPSIZE+1;
  392.                     break;
  393.                 }
  394.             }
  395.         /*printf( "(%d|%d) : (%d|%d)\n", wX0, wY0, wX1, wY1 );*/
  396.         rMag = wXSize / (REAL)(wX1 - wX0 + 1);
  397.         rTmp = wYSize / (REAL)(wY1 - wY0 + 1);
  398.         if( rMag > rTmp )
  399.             rMag = rTmp;
  400.         prj.rXScale *= rMag;
  401.         prj.rYScale *= rMag;
  402.         prj.wXOff -= rMag * wX0;
  403.         prj.wYOff -= rMag * wY0;
  404.         /*printf( "+ (%d|%d), * %f\n", prj.wXOff, prj.wYOff, rMag );*/
  405.         }
  406.  
  407.     prj.wXOff += wXMin + 1;
  408.     prj.wYOff += wYMin + 1;
  409.     }
  410.  
  411.  
  412. /*------------------------------------------------------------------------*\
  413.  
  414.   Actually draw a sketch of the cuboid for which we made room in
  415.   MakeScales().
  416.  
  417. \*------------------------------------------------------------------------*/
  418.  
  419. VOID DrawBox()
  420.     {
  421.     REAL rX, rY, rZ;
  422.     WORD wX0, wX1=0, wY0, wY1=0;
  423.     WORD wIdx;
  424.  
  425.     SetAPen( pwinMain->RPort, 0 );
  426.     RectFill( pwinMain->RPort,
  427.         pwinMain->BorderLeft,
  428.         pwinMain->BorderTop,
  429.         pwinMain->Width - pwinMain->BorderRight - 1,
  430.         pwinMain->Height - pwinMain->BorderBottom - 1 );
  431.     SetAPen( pwinMain->RPort, 1 );
  432.     SetDrMd( pwinMain->RPort, JAM1 );
  433.     /* Make projections of the eight vertices. */
  434.     rX = rY = rZ = 0;
  435.     for( wIdx = 0; wIdx < 8; wIdx++ )
  436.         {
  437.         wX0 = wX1;
  438.         wY0 = wY1;
  439.         Transform( rX, rY, rZ, &wX1, &wY1 );
  440.         if( wIdx == 0 )
  441.             Move( pwinMain->RPort, wX1, wY1 );
  442.         else
  443.             Draw( pwinMain->RPort, wX1, wY1 );
  444.         switch( wIdx )
  445.             {
  446.             case 0:
  447.                 rX += MAPSIZE+1;
  448.                 break;
  449.             case 1:
  450.                 rY += MAPSIZE+1;
  451.                 break;
  452.             case 2:
  453.                 rX -= MAPSIZE+1;
  454.                 break;
  455.             case 3:
  456.                 rZ += rPeak;
  457.                 break;
  458.             case 4:
  459.                 rX += MAPSIZE+1;
  460.                 break;
  461.             case 5:
  462.                 rY -= MAPSIZE+1;
  463.                 break;
  464.             case 6:
  465.                 rX -= MAPSIZE+1;
  466.                 break;
  467.             }
  468.         }
  469.     }
  470.  
  471.  
  472. /*------------------------------------------------------------------------*\
  473.  
  474.   Draw an image of the the previously calculated landscape.
  475.  
  476.   You must have called SetObserver() and MakeScales() already!
  477.  
  478. \*------------------------------------------------------------------------*/
  479.  
  480. VOID DrawMountain( REAL rSnowLine )
  481.     {
  482.     WORD wX0, wY0, wXStep, wYStep;
  483.     WORD wI, wJ, wX, wY, wIdx, wSwap, wPen;
  484.     WORD waX[ 5 ], waY[ 5 ];
  485.     REAL raX[ 5 ], raY[ 5 ], raZ[ 5 ];
  486.     REAL rZ0, rZ1;
  487. #define POINT( n, i, j ) raX[ n ] = i; raY[ n ] = j; raZ[ n ] = raMap[ i ][ j ]
  488.  
  489.     SetRast( pwinMain->RPort, 0 );
  490.     RefreshWindowFrame( pwinMain );
  491.     SetDrMd( pwinMain->RPort, JAM1 );
  492.  
  493.     /* Speed optimization: start by drawing a rectangle of blue ocean. */
  494.     SetAPen( pwinMain->RPort, laPens[ 16 ] );
  495.     if( fBorder )
  496.         SetOPen( pwinMain->RPort, laPens[ 0 ] );
  497.     Transform( 0, 0, 0, &wX, &wY );
  498.     AreaMove( pwinMain->RPort, wX, wY );
  499.     Transform( 0, MAPSIZE, 0, &wX, &wY );
  500.     AreaDraw( pwinMain->RPort, wX, wY );
  501.     Transform( MAPSIZE, MAPSIZE, 0, &wX, &wY );
  502.     AreaDraw( pwinMain->RPort, wX, wY );
  503.     Transform( MAPSIZE, 0, 0, &wX, &wY );
  504.     AreaDraw( pwinMain->RPort, wX, wY );
  505.     AreaEnd( pwinMain->RPort );
  506.     BNDRYOFF( pwinMain->RPort );
  507.  
  508.     /* For the hidden-line drawing to work correctly, we have to start at */
  509.     /* the rear (from the observer's pont of view) of the map. */
  510.     /* Also we may have to swap the order of the x- and y-loops. */
  511.     wSwap = ( prj.o.x * prj.o.x > prj.o.y * prj.o.y ) ? 1 : 0;
  512.     wXStep = ( prj.o.x > 0 ) ? 1 : -1;
  513.     wYStep = ( prj.o.y > 0 ) ? 1 : -1;
  514.     wX0 = ( wXStep > 0 ) ? 1 : MAPSIZE;
  515.     wY0 = ( wYStep > 0 ) ? 1 : MAPSIZE;
  516.  
  517.     wI = wJ = 0;
  518.     for( wI *= (1-wSwap), wJ *= wSwap; wI < MAPSIZE && wJ < MAPSIZE ; wI += wSwap, wJ += (1-wSwap) )
  519.         {
  520.         for( wI *= wSwap, wJ *= (1-wSwap); wI < MAPSIZE && wJ < MAPSIZE ; wI += (1-wSwap), wJ += wSwap )
  521.             {
  522.             if( CheckAbort() )
  523.                 goto quit;
  524.             wX = wX0 + wXStep * wI;
  525.             wY = wY0 + wYStep * wJ;
  526.             POINT( 0, wX,   wY   );     /* y ^     1  0   */
  527.             POINT( 1, wX-1, wY   );     /*   |            */
  528.             POINT( 2, wX-1, wY-1 );     /*   +->   2  3   */
  529.             POINT( 3, wX,   wY-1 );     /*     x          */
  530.             rZ0 = rPeak;
  531.             rZ1 = 0;
  532.             for( wIdx = 0; wIdx < 4; wIdx++ )
  533.                 {
  534.                 if( rZ0 > raZ[ wIdx ] )
  535.                     rZ0 = raZ[ wIdx ];
  536.                 if( rZ1 < raZ[ wIdx ] )
  537.                     rZ1 = raZ[ wIdx ];
  538.                 }
  539.             /* The fifth point is the center of the current square. */
  540.             raX[ 4 ] = wX - 0.5;
  541.             raY[ 4 ] = wY - 0.5;
  542.             raZ[ 4 ] = rZ0 + (0.25 + 0.5*Random()) * (rZ1 - rZ0);
  543.             for( wIdx = 0; wIdx < 5; wIdx++ )
  544.                 Transform( raX[ wIdx ], raY[ wIdx ], raZ[ wIdx ], &waX[ wIdx ], &waY[ wIdx ] );
  545.             /* Paint the surface of this square, as four triangles. */
  546.             for( wIdx = 0; wIdx < 4; wIdx++ )
  547.                 {
  548.                 wPen = GetShadePen( raZ[ 4 ], raZ[ wIdx ], raZ[ (wIdx+1) % 4 ],
  549.                             rSnowLine );
  550.                 if( wPen == 16 )
  551.                     continue;   /* optimization for the ocean, see above */
  552.                 SetAPen( pwinMain->RPort, laPens[ wPen ] );
  553.                 AreaMove( pwinMain->RPort, waX[ 4 ], waY[ 4 ] );
  554.                 AreaDraw( pwinMain->RPort, waX[ wIdx ], waY[ wIdx ] );
  555.                 AreaDraw( pwinMain->RPort, waX[ (wIdx+1) % 4 ], waY[ (wIdx+1) % 4 ] );
  556.                 AreaEnd( pwinMain->RPort );
  557.                 }
  558.             /* Skip the following black border stuff if this was a square */
  559.             /* full of ocean. Even if we have a thin black line around the */
  560.             /* whole map this test is important, because otherwise that line */
  561.             /* tends to look not so thin any more (rather a little lumpy). */
  562.             if( raZ[ 0 ] <= 0 && raZ[ 1 ] <= 0 && raZ[ 2 ] <= 0 && raZ[ 3 ] <= 0 )
  563.                 continue;
  564.             /* Close the end of each row with a black vertical wall. */
  565.             /* Note that we can reuse two of the transformed corners of */
  566.             /* the surface that we have just drawn, but which two depends */
  567.             /* on whether we were going backwards or forward. */
  568.             if( wI == MAPSIZE - 1 )
  569.                 {
  570.                 if( wX == 1 )   /* keep points 1 and 2 */
  571.                     {
  572.                     Transform( raX[ 1 ], raY[ 1 ], 0, &waX[ 0 ], &waY[ 0 ] );
  573.                     Transform( raX[ 2 ], raY[ 2 ], 0, &waX[ 3 ], &waY[ 3 ] );
  574.                     }
  575.                 else            /* keep points 0 and 3 */
  576.                     {
  577.                     Transform( raX[ 0 ], raY[ 0 ], 0, &waX[ 1 ], &waY[ 1 ] );
  578.                     Transform( raX[ 3 ], raY[ 3 ], 0, &waX[ 2 ], &waY[ 2 ] );
  579.                     }
  580.                 SetAPen( pwinMain->RPort, laPens[ 0 ] );
  581.                 AreaMove( pwinMain->RPort, waX[ 0 ], waY[ 0 ] );
  582.                 for( wIdx = 1; wIdx < 4; wIdx++ )
  583.                     AreaDraw( pwinMain->RPort, waX[ wIdx ], waY[ wIdx ] );
  584.                 AreaEnd( pwinMain->RPort );
  585.                 }
  586.             /* The same for the other (i.e. Y-) direction. */
  587.             /* Oh, one more thing: If both if()-blocks are TRUE (which */
  588.             /* happens on the frontmost corner of the map), we will first */
  589.             /* have to recalculate the transformed corners of the surface. */
  590.             if( wI == MAPSIZE - 1 && wJ == MAPSIZE - 1 )
  591.                 for( wIdx = 0; wIdx < 4; wIdx++ )
  592.                     Transform( raX[ wIdx ], raY[ wIdx ], raZ[ wIdx ], &waX[ wIdx ], &waY[ wIdx ] );
  593.             if( wJ == MAPSIZE - 1 )
  594.                 {
  595.                 if( wY == 1 )   /* keep points 2 and 3 */
  596.                     {
  597.                     Transform( raX[ 2 ], raY[ 2 ], 0, &waX[ 1 ], &waY[ 1 ] );
  598.                     Transform( raX[ 3 ], raY[ 3 ], 0, &waX[ 0 ], &waY[ 0 ] );
  599.                     }
  600.                 else            /* keep points 0 and 1 */
  601.                     {
  602.                     Transform( raX[ 1 ], raY[ 1 ], 0, &waX[ 2 ], &waY[ 2 ] );
  603.                     Transform( raX[ 0 ], raY[ 0 ], 0, &waX[ 3 ], &waY[ 3 ] );
  604.                     }
  605.                 SetAPen( pwinMain->RPort, laPens[ 0 ] );
  606.                 AreaMove( pwinMain->RPort, waX[ 0 ], waY[ 0 ] );
  607.                 for( wIdx = 1; wIdx < 4; wIdx++ )
  608.                     AreaDraw( pwinMain->RPort, waX[ wIdx ], waY[ wIdx ] );
  609.                 AreaEnd( pwinMain->RPort );
  610.                 }
  611.             }
  612.         wI *= wSwap;
  613.         wJ *= (1-wSwap);
  614.         }
  615. quit:
  616.     RefreshWindowFrame( pwinMain );
  617.     }
  618.  
  619.  
  620.  
  621. /*------------------------------------------------------------------------*\
  622.  
  623.   Call this during lengthy operations.
  624.  
  625.   Will return TRUE if the user has done something that should make us
  626.   abort (which can be ESC, the cursor keys, or any gadget or menu
  627.   action).
  628.  
  629. \*------------------------------------------------------------------------*/
  630.  
  631. BOOL CheckAbort()
  632.     {
  633.     struct IntuiMessage *intuiMsg;
  634.     BOOL fResult = FALSE;
  635.  
  636.     intuiMsg = GT_GetIMsg( pwinMain->UserPort );
  637.     if( intuiMsg == NULL )
  638.         return FALSE;
  639.  
  640.     switch( intuiMsg->Class )
  641.         {
  642.         case IDCMP_MENUPICK:
  643.         case IDCMP_NEWSIZE:
  644.         case IDCMP_CLOSEWINDOW:
  645.             fResult = TRUE;
  646.             break;
  647.         case IDCMP_GADGETDOWN:
  648.             wHotGadget = ((struct Gadget *)(intuiMsg->IAddress))->GadgetID;
  649.             fResult = TRUE;
  650.             break;
  651.         case IDCMP_GADGETUP:
  652.             wHotGadget = IDC_NONE;
  653.             fResult = TRUE;
  654.             break;
  655.         case IDCMP_VANILLAKEY:
  656.             if( intuiMsg->Code == 27 )
  657.                 fResult = TRUE;
  658.             break;
  659.         case IDCMP_RAWKEY:
  660.             switch( intuiMsg->Code )
  661.                 {
  662.                 case CURSORUP:
  663.                 case CURSORDOWN:
  664.                 case CURSORLEFT:
  665.                 case CURSORRIGHT:
  666.                     fResult = TRUE;
  667.                     break;
  668.                 }
  669.             break;
  670.         }
  671.     GT_ReplyIMsg( intuiMsg );
  672.  
  673.     return fResult;
  674.     }
  675.  
  676.  
  677.  
  678. /*------------------------------------------------------------------------*\
  679.  
  680.   Processing of input events.
  681.  
  682. \*------------------------------------------------------------------------*/
  683.  
  684. BOOL HandleGui()
  685.     {
  686.     REAL rScale = 0.5;
  687.     REAL rR = 2.0, rPhi, rTheta;
  688.     struct IntuiMessage *intuiMsg, imCopy;
  689.     struct MenuItem *item;
  690.     LONG lMenuID;
  691.     BOOL fShift, fAutoDraw = FALSE, fQuit = FALSE;
  692.     WORD wAutoCount = -1;
  693.     WORD wRedraw;       /* 0: don't redraw, 1: sketch, 2: full */
  694.     UWORD wOld = 0;     /* used to determine actual movement */
  695.  
  696.     MakeMountain( rScale );
  697.     rTheta = (90.0 * piRise.VertPot) / MAXPOT;
  698.     rPhi = (360.0 * piSpin.HorizPot) / MAXPOT;
  699.     wRedraw = 1;
  700.     while( !fQuit )
  701.         {
  702.         if( wRedraw )
  703.             {
  704.             SetObserver( rR, rPhi, rTheta );
  705.             MakeScales( pwinMain->BorderLeft,
  706.                         pwinMain->Width  - pwinMain->BorderRight - 1,
  707.                         pwinMain->BorderTop,
  708.                         pwinMain->Height - pwinMain->BorderBottom - 1 );
  709.             if( wRedraw > 1 )
  710.                 {
  711.                 DrawMountain( rSnowLine );
  712.                 wAutoCount = -1;
  713.                 }
  714.             else
  715.                 {
  716.                 DrawBox();
  717.                 if( fAutoDraw )
  718.                     wAutoCount = 10;    /* redraw at 1 sec from now */
  719.                 }
  720.             wRedraw = 0;
  721.             }
  722.  
  723.         intuiMsg = GT_GetIMsg( pwinMain->UserPort );
  724.         if( intuiMsg == NULL )
  725.             {
  726.             Wait( -1 );
  727.             continue;
  728.             }
  729.  
  730.         /* We've got a message. Reply to it ASAP, then process it. */
  731.         imCopy = *intuiMsg;
  732.         GT_ReplyIMsg( intuiMsg );
  733.         fShift = imCopy.Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT);
  734.         switch( imCopy.Class )
  735.             {
  736.             case IDCMP_GADGETDOWN:
  737.                 wHotGadget = ((struct Gadget *)(imCopy.IAddress))->GadgetID;
  738.                 break;
  739.             case IDCMP_INTUITICKS:
  740.                 /* At each tick check the prop gadgets. */
  741.                 if( wHotGadget == IDC_HEIGHT )
  742.                     {
  743.                     if( wOld != piRise.VertPot )
  744.                         {
  745.                         wOld = piRise.VertPot;
  746.                         rTheta = (90.0 * wOld) / MAXPOT;
  747.                         wRedraw = 1;
  748.                         }
  749.                     }
  750.                 else if( wHotGadget == IDC_ROTATION )
  751.                     {
  752.                     if( wOld != piSpin.HorizPot )
  753.                         {
  754.                         wOld = piSpin.HorizPot;
  755.                         rPhi = (360.0 * wOld) / MAXPOT;
  756.                         wRedraw = 1;
  757.                         }
  758.                     }
  759.                 else
  760.                 /* If the prop gadgets are free, count down for auto redraw. */
  761.                     {
  762.                     if( wAutoCount == 0 )
  763.                         wRedraw = 2;
  764.                     if( wAutoCount >= 0 )
  765.                         wAutoCount--;
  766.                     }
  767.                 break;
  768.             case IDCMP_GADGETUP:
  769.                 switch( ((struct Gadget *)(imCopy.IAddress))->GadgetID )
  770.                     {
  771.                     case IDC_ROTATION:
  772.                     case IDC_HEIGHT:
  773.                         rTheta = (90.0 * piRise.VertPot) / MAXPOT;
  774.                         rPhi = (360.0 * piSpin.HorizPot) / MAXPOT;
  775.                         wRedraw = 1;
  776.                         break;
  777.                     case IDC_STOP_GO:
  778.                         wRedraw = 2;
  779.                         break;
  780.                     }
  781.                 wHotGadget = IDC_NONE;
  782.                 break;
  783.             case IDCMP_VANILLAKEY:
  784.                 switch( imCopy.Code )
  785.                     {
  786.                     case 13:
  787.                         wRedraw = 2;
  788.                         break;
  789.                     }
  790.                 break;
  791.             case IDCMP_RAWKEY:
  792.                 switch( imCopy.Code )
  793.                     {
  794.                     case CURSORUP:
  795.                         rTheta -= fShift ? 30 : 5;
  796.                         wRedraw = 1;
  797.                         break;
  798.                     case CURSORDOWN:
  799.                         rTheta += fShift ? 30 : 5;
  800.                         wRedraw = 1;
  801.                         break;
  802.                     case CURSORLEFT:
  803.                         rPhi -= fShift ? 30 : 5;
  804.                         wRedraw = 1;
  805.                         break;
  806.                     case CURSORRIGHT:
  807.                         rPhi += fShift ? 30 : 5;
  808.                         wRedraw = 1;
  809.                         break;
  810.                     }
  811.                 if( rTheta < 0 )
  812.                     rTheta = 0;
  813.                 if( rTheta > 90 )
  814.                     rTheta = 90;
  815.                 if( rPhi < 0 )
  816.                     rPhi = 0;
  817.                 if( rPhi > 360 )
  818.                     rPhi = 360;
  819.                 NewModifyProp( &gadRise, pwinMain, NULL,
  820.                     piRise.Flags, 0, (rTheta / 90) * MAXPOT, 0,
  821.                     piRise.VertBody, 1 );
  822.                 NewModifyProp( &gadSpin, pwinMain, NULL,
  823.                     piSpin.Flags, (rPhi / 360) * MAXPOT, 0,
  824.                     piSpin.HorizBody, 0, 1 );
  825.                 break;
  826.             case IDCMP_MENUPICK:
  827.                 while( imCopy.Code != MENUNULL )
  828.                     {
  829.                     item = ItemAddress( pwinMain->MenuStrip, imCopy.Code );
  830.                     lMenuID = (LONG)GTMENUITEM_USERDATA( item );
  831.                     switch( lMenuID )
  832.                         {
  833.                         case IDM_NEW:
  834.                             MakeMountain( rScale );
  835.                             wRedraw = 1;
  836.                             break;
  837.                         case IDM_BORDER:
  838.                             fBorder = item->Flags & CHECKED;
  839.                             break;
  840.                         case IDM_AUTODRAW:
  841.                             fAutoDraw = item->Flags & CHECKED;
  842.                             break;
  843.                         case IDM_DRAW:
  844.                             wRedraw = 2;
  845.                             break;
  846.                         case IDM_QUIT:
  847.                             fQuit = TRUE;
  848.                             break;
  849.                         case IDM_MAX:
  850.                             {
  851.                             struct Screen *scr = pwinMain->WScreen;
  852.                             ChangeWindowBox( pwinMain, 0, scr->BarHeight + 1,
  853.                                 scr->Width, scr->Height - scr->BarHeight - 1 );
  854.                             break;
  855.                             }
  856.                         case IDM_CENTER:
  857.                             {
  858.                             struct Screen *scr = pwinMain->WScreen;
  859.                             ChangeWindowBox( pwinMain,
  860.                                 scr->Width / 4, scr->Height / 4,
  861.                                 scr->Width / 2, scr->Height / 2 );
  862.                             break;
  863.                             }
  864.                         case IDM_ZOOM:
  865.                             ZipWindow( pwinMain );
  866.                             break;
  867.                         }
  868.                     imCopy.Code = item->NextSelect;
  869.                     }
  870.                 break;
  871.             case IDCMP_NEWSIZE:
  872.                 wRedraw = 1;
  873.                 break;
  874.             case IDCMP_CLOSEWINDOW:
  875.                 fQuit = TRUE;
  876.                 break;
  877.             }
  878.         }
  879.     return TRUE;
  880.     }
  881.  
  882.  
  883.  
  884. /*------------------------------------------------------------------------*\
  885.  
  886.   Main entry point.
  887.  
  888. \*------------------------------------------------------------------------*/
  889.  
  890. int main( int argc, char *argv[] )
  891.     {
  892.     atexit( GetMeOutOfHere );
  893.     if( !StartMeUp() )
  894.         return 10;
  895.     if( argc > 1 )
  896.         fGfxV39 = FALSE;     /* FIXME */
  897.     atexit( DestroyGui );
  898.     if( !BuildGui( TRUE ) )
  899.         return 10;
  900.     HandleGui();
  901.     return 0;
  902.     }
  903.  
  904.